package com.atguigu.security.config;

import com.atguigu.security.component.AppReactiveUserDetailsService;
import com.atguigu.security.component.CustomAuthenticationManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * @author lfy
 * @Description
 * @create 2023-12-24 21:39
 */
@Configuration
@EnableReactiveMethodSecurity //开启响应式 的 基于方法级别的权限控制
public class AppSecurityConfiguration {
    @Primary
    @Bean
    PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Autowired
    ReactiveUserDetailsService appReactiveUserDetailsService;

    @Bean
    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        //1、定义哪些请求需要认证，哪些不需要
        http.authorizeExchange(authorize -> {
            //1.1、允许所有人都访问静态资源；
            authorize.matchers(PathRequest.toStaticResources()
                    .atCommonLocations()).permitAll();

            //1.2、剩下的所有请求都需要认证（登录）
            authorize.anyExchange().authenticated();
        });

        //2、开启默认的表单登录
        http.formLogin(e -> {
//            e.loginPage("/haha");
            e.authenticationFailureHandler(customAuthenticationFailureHandler()).authenticationSuccessHandler(customAuthenticationSuccessHandler());
        });

        //3、安全控制:
        http.csrf(ServerHttpSecurity.CsrfSpec::disable);

        // 目前认证： 用户名 是 user  密码是默认生成。
        // 期望认证： 去数据库查用户名和密码

        //4、配置 认证规则： 如何去数据库中查询到用户;
        // Spring Security 底层使用 ReactiveAuthenticationManager 去查询用户信息
        // ReactiveAuthenticationManager 有一个实现是
        //   UserDetailsRepositoryReactiveAuthenticationManager： 用户信息去数据库中查
        //   UDRespAM 需要  ReactiveUserDetailsService：
        // 我们只需要自己写一个 ReactiveUserDetailsService： 响应式的用户详情查询服务
        http.authenticationManager(
            new CustomAuthenticationManager(appReactiveUserDetailsService, passwordEncoder())
        );

//        http.addFilterAt()

        //构建出安全配置
        return http.build();
    }

    @Bean
    public ServerAuthenticationSuccessHandler customAuthenticationSuccessHandler() {
        // 使用 RedirectServerAuthenticationSuccessHandler 指定跳转页面
        RedirectServerAuthenticationSuccessHandler handler = new RedirectServerAuthenticationSuccessHandler();
        handler.setLocation(URI.create("/hello")); // 登录成功后跳转到 /hello
        return handler;
    }

    @Bean
    public ServerAuthenticationFailureHandler customAuthenticationFailureHandler() {
        return (WebFilterExchange wfe, AuthenticationException exception) -> {
            // 根据异常类型返回详细错误信息
            String errorMessage = "Invalid credentials";
            if (exception instanceof BadCredentialsException) {
                errorMessage = "username or password is wrong";
            } else if (exception.getCause() instanceof UsernameNotFoundException) {
                errorMessage = "username not exists";
            }

            // 将错误信息写入响应或重定向到错误页面
            wfe.getExchange().getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return wfe.getExchange().getResponse()
                    .writeWith(Mono.just(wfe.getExchange().getResponse()
                            .bufferFactory()
                            .wrap(("{\"error\": \"" + errorMessage + "\"}").getBytes())));
        };
    }
}
